package com.codebase.alicloud.oss;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;

public class HttpCfgHelper {
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpCfgHelper.class);
    private static final int MAX_TIMEOUT = 30000; //超时时间
    private static final int MAX_TOTAL = 500; //最大连接数
    private static final int ROUTE_MAX_TOTAL = 100; //每个路由基础的连接数
    private static final int MAX_RETRY = 3; //重试次数
    private static PoolingHttpClientConnectionManager connMgr; //连接池
    private static HttpRequestRetryHandler retryHandler; //重试机制

    static {
        cfgPoolMgr();
        cfgRetryHandler();
    }

    private static void cfgPoolMgr() {
        ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
        LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", plainsf)
                .register("https", sslsf)
                .build();
        connMgr = new PoolingHttpClientConnectionManager(registry);
        connMgr.setMaxTotal(MAX_TOTAL);
        connMgr.setDefaultMaxPerRoute(ROUTE_MAX_TOTAL);
        connMgr.setValidateAfterInactivity(60000);
    }

    /**
     * 设置重试机制
     */
    private static void cfgRetryHandler() {
        retryHandler = (e, excCount, ctx) -> {
            //超过最大重试次数，就放弃
            if (excCount > MAX_RETRY) {
                return false;
            }
            //服务器丢掉了链接，就重试
            if (e instanceof NoHttpResponseException) {
                return true;
            }
            //不重试SSL握手异常
            if (e instanceof SSLHandshakeException) {
                return false;
            }
            //中断
            if (e instanceof InterruptedIOException) {
                return false;
            }
            //目标服务器不可达
            if (e instanceof UnknownHostException) {
                return false;
            }
            //SSL异常
            if (e instanceof SSLException) {
                return false;
            }
            HttpClientContext clientCtx = HttpClientContext.adapt(ctx);
            HttpRequest req = clientCtx.getRequest();
            //如果是幂等请求，就再次尝试
            return !(req instanceof HttpEntityEnclosingRequest);
        };
    }


    /**
     * 不允许外部创建实例
     */
    private HttpCfgHelper() {
    }

    public static JSONObject sendHttp(String url, Map<String, String> headers, Object param) {
        HttpRequestBase reqBase = new HttpPost(url);
        reqBase.setProtocolVersion(HttpVersion.HTTP_1_1);
        LOGGER.info("\n--->>开始向地址[{}]发起 [{}] 请求", url, reqBase.getMethod());
        LOGGER.info("--->>请求头为{}", JSON.toJSONString(headers));
        long startTime = System.currentTimeMillis();
        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connMgr)
                .setRetryHandler(retryHandler)
//                .evictIdleConnections(60, TimeUnit.SECONDS)//空闲时间
//                .disableAutomaticRetries() // 关闭自动重试
                .build();
        // 配置请求的超时设置
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(MAX_TIMEOUT)
                .setConnectTimeout(MAX_TIMEOUT)
                .setSocketTimeout(MAX_TIMEOUT)
                .build();
        reqBase.setConfig(requestConfig);

        //设置请求头
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                reqBase.setHeader(entry.getKey(), entry.getValue());
            }
        }
        //响应对象
        CloseableHttpResponse res = null;
        //响应内容
        String resCtx = null;
        JSONObject resJs = new JSONObject();
        try {
            //添加参数 参数是json字符串
            if (param instanceof String) {
                String paramStr = String.valueOf(param);
                ((HttpEntityEnclosingRequest) reqBase).setEntity(new StringEntity(String.valueOf(paramStr), ContentType.create("application/json", "UTF-8")));
            } else if (param instanceof byte[]) {
                byte[] paramBytes = (byte[]) param;
                ((HttpEntityEnclosingRequest) reqBase).setEntity(new ByteArrayEntity(paramBytes));
            } else if (param instanceof File) {
                File paramFile = (File) param;
                ((HttpEntityEnclosingRequest) reqBase).setEntity(new FileEntity(paramFile));
            } else if (param instanceof List) {
                List paramForm = (List) param;
                ((HttpEntityEnclosingRequest) reqBase).setEntity(new UrlEncodedFormEntity(paramForm, "UTF-8"));
            } else if (param instanceof HttpEntity) {
                ((HttpEntityEnclosingRequest) reqBase).setEntity((HttpEntity) param);
            }
            //执行请求
            res = httpClient.execute(reqBase);
            LOGGER.info("--->>执行请求完毕，响应状态：{}", res.getStatusLine());
            if (res.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new RuntimeException("--->>HTTP访问异常:" + res.getStatusLine());
            }

            //获取请求响应对象和响应entity
            HttpEntity httpEntity = res.getEntity();
            if (httpEntity != null) {
                resCtx = EntityUtils.toString(httpEntity, "utf-8");
                LOGGER.info("--->>获取响应内容：{}", resCtx);
            } else {
                LOGGER.info("response_httpEntity_is_null：{}", httpEntity);
            }
            Header[] allHeaders = res.getAllHeaders();
            for (Header resHeader : allHeaders) {
                resJs.put(resHeader.getName(), resHeader.getValue());
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("文件上传失败", e);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("请求失败", e);
        } finally {
            if (res != null) {
                try {
                    res.close();
                } catch (IOException e) {
                    throw new RuntimeException("--->>关闭请求响应失败", e);
                }
            }
        }
        long endTime = System.currentTimeMillis();
        LOGGER.info("--->>请求执行完毕，耗费时长：{} 秒", (endTime - startTime) / 1000);
        resJs.put("data", resCtx);
        return resJs;
    }
}
